package com.imachen.ymcc.service.impl;

import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.imachen.ymcc.LoginService;
import com.imachen.ymcc.constants.RedisSmsConstants;
import com.imachen.ymcc.domain.*;
import com.imachen.ymcc.dto.PhoneRegisterDto;
import com.imachen.ymcc.dto.RedisSmsDto;
import com.imachen.ymcc.enums.GlobalExceptionCode;
import com.imachen.ymcc.mapper.UserMapper;
import com.imachen.ymcc.result.JSONResult;
import com.imachen.ymcc.service.*;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.imachen.ymcc.util.AssertUtil;
import com.imachen.ymcc.util.BitStatesUtils;
import com.imachen.ymcc.util.StrUtils;
import com.sun.org.apache.bcel.internal.generic.NEW;
import io.seata.spring.annotation.GlobalTransactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Set;

/**
 * <p>
 * 会员登录账号 服务实现类
 * </p>
 *
 * @author imachen
 * @since 2022-09-23
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private LoginService loginService;

    @Autowired
    private IUserBaseInfoService userBaseInfoService;

    @Autowired
    private IUserAccountService userAccountService;

    @Autowired
    private IUserCodeService userCodeService;

    @Autowired
    private IUserReferrerService userReferrerService;

    @Autowired
    private IUserIntegralLogService userIntegralLogService;

    @Autowired
    private IUserGrowLogService userGrowLogService;

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    public void register(PhoneRegisterDto phoneRegisterDto) {

        String mobile = phoneRegisterDto.getMobile();
        String password = phoneRegisterDto.getPassword();
        Integer regChannel = phoneRegisterDto.getRegChannel();
        String smsCode = phoneRegisterDto.getSmsCode();
        String code = phoneRegisterDto.getCode();

        // 1.参数校验使用JSR 303 完成
        // 2.拼接业务键，去redis中获取短信验证码，进行校验
        String rediskey = String.format(RedisSmsConstants.REDIS_REGISTER_PHONE_KEY, mobile);
        Object codeObj = redisTemplate.opsForValue().get(rediskey);
        // 2.1.如果短信验证码为空，那么抛出异常
        AssertUtil.isNotNull(codeObj, GlobalExceptionCode.CODE_IS_NULL_ERROR);
             // 2.2.判断短信验证码是否正确
        RedisSmsDto redisSmsDto = (RedisSmsDto) codeObj;
        AssertUtil.isEquals(smsCode,redisSmsDto.getCode(),GlobalExceptionCode.CODE_ERROR);

        // 3.根据手机号查询用户信息
        User user = selectUser(mobile);
              // 3.1.判断用户是否已注册
        AssertUtil.isNull(user, GlobalExceptionCode.USER_EXISTED_ERROR);

        // 校验注册邀请码是否使用, 一个推荐码只能用作一次注册
        // 查询推荐人信息表，如果有该code的信息 则使用过
        Wrapper<UserReferrer> wrapper = new EntityWrapper<>();
        wrapper.eq("code",code);
        UserReferrer userReferrer = userReferrerService.selectOne(wrapper);
        AssertUtil.isNull(userReferrer,GlobalExceptionCode.USER_CODE_USED_ERROR);

        // 4.保存login
        JSONResult jsonResult = saveLogin(mobile,password);
             // 4.1.断言jsonResult是否成功
        AssertUtil.isTrue(jsonResult.isSuccess(),GlobalExceptionCode.USER_SAVE_ERROR);
             // 4.2.断言ID是否为null
        AssertUtil.isNotNull(jsonResult.getData(),GlobalExceptionCode.USER_SAVE_ERROR);
             // 4.3.得到id
        Long LoginId = Long.valueOf(jsonResult.getData().toString());

        // 5.保存user
        // 5.1.保存用户信息
        Long userId = saveUser(LoginId,mobile);
        // 保存注册人与推荐人信息
        Long referrerId = saveUserReferrer(code, userId);
        // 5.2.保存用户基本信息
        saveUserBaseInfo(userId,regChannel,referrerId);
        // 5.3.保存用户账号信息
        saveUserAccount(userId);

        // 6.保存推荐码信息
        saveUserCode(userId);



    }

    // 保存注册人与推荐人信息
    private Long saveUserReferrer(String code, Long userId) {
        if (!StringUtils.isEmpty(code)){
            // 根据code查询推荐人id
            Wrapper<UserCode> wrapper = new EntityWrapper<>();
            wrapper.eq("code",code);
            UserCode userCode = userCodeService.selectOne(wrapper);
            Long referrerId = userCode.getUserId();
            // 保存信息
            UserReferrer userReferrer = new UserReferrer();
            userReferrer.setCode(code);
            userReferrer.setUserId(userId);
            userReferrer.setReferrerId(referrerId);
            userReferrerService.insert(userReferrer);

            // 给推荐人送积分
            giveIntegral(referrerId);
            // 给推荐人送成长值
            userBaseInfoService.giveGrowScore(referrerId);
            //giveGrowScore(userId,referrerId);
            return referrerId;
        }
        return null;
    }

//    // 给推荐人送成长值
//    private void giveGrowScore(Long userId,Long referrerId) {
//        UserBaseInfo userBaseInfo = userBaseInfoService.selectById(referrerId);
//        userBaseInfo.setGrowScore(userBaseInfo.getGrowScore() + 10);
//        userBaseInfoService.updateById(userBaseInfo);
//        // 添加日志表t_user_grow_log
//        UserGrowLog userGrowLog = new UserGrowLog();
//        userGrowLog.setCreateTime(new Date());
//        userGrowLog.setUserId(userId);
//        userGrowLog.setFromReason("推荐新人注册");
//        userGrowLog.setScore(10);
//        userGrowLogService.insert(userGrowLog);
//    }

    // 给推荐人送积分
    private void giveIntegral(Long referrerId) {
        Wrapper<User> wrapper = new EntityWrapper<>();
        wrapper.eq("id",referrerId);
        User user = selectOne(wrapper);
        user.setIntegral(user.getIntegral()+10);
        updateById(user);
        // 记录日志表t_user_integral_log
        UserIntegralLog userIntegralLog = new UserIntegralLog();
        userIntegralLog.setUserId(user.getId());
        userIntegralLog.setIntegral("推荐人获得了积分：" + 10);
        userIntegralLog.setCurrentTime(new Date());
        userIntegralLogService.insert(userIntegralLog);
    }


    // 保存生成用户注册邀请码
    private void saveUserCode(Long userId) {
        UserCode userCode = new UserCode();
        userCode.setUserId(userId);
        // 生成该用户的推荐码
        String code = StrUtils.getRandomString(4);
        userCode.setCode(code);
        userCodeService.insert(userCode);

    }

    //保存用户账号信息
    private void saveUserAccount(Long userId) {
        UserAccount userAccount = new UserAccount();
        userAccount.setId(userId);
        userAccount.setCreateTime(new Date().getTime());
        userAccount.setUsableAmount(new BigDecimal(0));
        userAccount.setFrozenAmount(new BigDecimal(0));
        // 设置默认密码，发短信给用户 @TODO
        userAccount.setPassword("123456");

        userAccountService.insert(userAccount);
    }

    //保存用户基本信息
    private void saveUserBaseInfo(Long userId, Integer regChannel,Long referrerId) {
        UserBaseInfo userBaseInfo = new UserBaseInfo();
        userBaseInfo.setCreateTime(new Date().getTime());
        userBaseInfo.setRegChannel(regChannel);
        userBaseInfo.setId(userId);
        userBaseInfo.setReferId(referrerId);
        userBaseInfoService.insert(userBaseInfo);
    }

    //保存用户信息
    private Long saveUser(Long loginId, String mobile) {
        User user = new User();
        user.setCreateTime(new Date().getTime());
        user.setPhone(mobile);
        user.setNickName(mobile);
        user.setLoginId(loginId);
        user.setIntegral(0);

        // 设置用户初始位状态
        long stateTmp = BitStatesUtils.addState(BitStatesUtils.OP_REGISTED, BitStatesUtils.OP_ACTIVED);
        long state = BitStatesUtils.addState(stateTmp, BitStatesUtils.OP_AUTHED_PHONE);
        user.setBitState(state);

        insert(user);
        return user.getId();
    }


    //保存login
    private JSONResult saveLogin(String mobile, String password) {
        Login login = new Login();
        login.setUsername(mobile);
        // 使用security的密码加密器进行密码加密
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        login.setPassword(bCryptPasswordEncoder.encode(password));
        login.setType(Login.USER_TYPE);
        login.setEnabled(Login.ENABLE);

        return loginService.saveOrUpdate(login);
    }


    // 根据电话号码查询用户信息
    public User selectUser(String mobile) {
        // 1.创建user对象的条件构造器
        Wrapper<User> wrapper = new EntityWrapper<>();
        // 2.设置查询条件
        wrapper.eq("phone",mobile);
        // 3.调用父类方法进行查询
        return selectOne(wrapper);
    }
}
